﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Speech.Recognition;
using CPLayer;
using System.ComponentModel;
using System.Windows.Forms;

namespace Supply_Fetch.MUILayer
{
    /// <summary>
    /// This class acts as a main controller for the Supply Fetch system, it also manages data traffic between the MUI and CP Layer.
    /// </summary>
    class UIProcessManager
    {

        private VoiceAcquisitionDevice audioInput;
        private SpeechToTextEngine speechToTextEngine;
        private LoadItemsController loadItemsController;
        private TestLEDController testLEDController;
        private FindItemController findItemController;
        private RemoveItemController removeItemController;
        private AddItemController addItemController;
        private GUI gui;

        private bool listenWorkerCancelled;
        private bool speechRecognized;

        /// <summary>
        /// Contructor.
        /// </summary>
        public UIProcessManager()
        {
            loadItemsController = new LoadItemsController();
            audioInput = new VoiceAcquisitionDevice();
            gui = new GUI();
            testLEDController = new TestLEDController();
            findItemController = new FindItemController(gui);
            removeItemController = new RemoveItemController();
            addItemController = new AddItemController();
            speechToTextEngine = new SpeechToTextEngine();

            gui.guiEventSubscription += new GUIEventSubscription(GUIEventHandler);
            speechToTextEngine.speechEventHandler += new SpeechEventHandler(SpeechHandler);
            gui.speechRecognitionWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.speechRecognitionWorker_DoWork);
            gui.speechRecognitionWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.speechRecognitionWorker_RunWorkerComplete);

            listenWorkerCancelled = false;
            speechRecognized = false;

            //Load current items available in the database.
            this.GetAllItems();

        }

        /// <summary>
        /// This handles any speech events that are sent by the SpeechToTextEngine.
        /// </summary>
        /// <param name="sender">Information about sender of event.</param>
        /// <param name="e">Contains the string value of word recognize by the system.</param>
        public void SpeechHandler(Item fItem)
        {
            this.FindItem(fItem);
        }

        /// <summary>
        /// This handles actions sent by the GUI.
        /// </summary>
        /// <param name="action">A generic object containing information about the action selected by the user.</param>
        private void GUIEventHandler(object action)
        {
            if (action != null)
            {
                if (action.GetType() == typeof(FindAction))
                {
                    this.FindItem(((FindAction)action).FItem);
                }
                else if (action.GetType() == typeof(AddAction))
                {
                    this.AddItem(((AddAction)action).Item, ((AddAction)action).Location);
                }
                else if (action.GetType() == typeof(RemoveAction))
                {
                    this.RemoveItem(((RemoveAction)action).RItem, ((RemoveAction)action).Location);
                }
                else if (action.GetType() == typeof(HardwareAction))
                {
                    this.HardwareActionHandler((HardwareAction)action);
                }
                else if (action.GetType() == typeof(ActivateMicAction))
                {
                    gui.speechRecognitionWorker.RunWorkerAsync();
                }
                else if (action.GetType() == typeof(DeactivateMicAction))
                {
                    gui.speechRecognitionWorker.CancelAsync();
                }
                else if (action.GetType() == typeof(MoveAction))
                {
                    MoveAction tempMoveAction = (MoveAction)action;
                    this.MoveItem(tempMoveAction.MItem, tempMoveAction.FromLocation, tempMoveAction.ToLocation);
                }
                else if (action.GetType() == typeof(CloseAction))
                {
                    this.CloseApplication();
                }
                else
                {
                    gui.presentFeedback("I'm sorry. I could not correctly process the requeste action. \n\nPlease try again.");
                }
            }
        }

        public void HardwareActionHandler( HardwareAction action )
        {
            if(action.State == Constants.LEDActionCodes.TURN_ALL_ON)
            {
                this.testLEDController.TurnAllLightsOn();
            }
            else if(action.State == Constants.LEDActionCodes.TURN_ALL_OFF)
            {
                this.testLEDController.TurnAllLightsOff();
            }
            else if(action.State == Constants.LEDActionCodes.ON_OR_OFF)
            {
                this.testLEDController.TestLED(action.led);
            }
            else
                /*Do nothing cause wrong.*/;
        }

        public void speechRecognitionWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.speechToTextEngine.ActivateListenMode();
            float confidence;
            for (int i = 1; i <= 100; i++)
            {
                WaitTime.millisecs(70);

                // If the speechRecognitionWorker.CancellationPending property is true, cancel
                if (gui.speechRecognitionWorker.CancellationPending)
                {
                    listenWorkerCancelled = true;
                    break;
                }//if a word is recognized, cancel
                if (this.speechToTextEngine.SpeechRecognized)
                {
                    confidence = this.speechToTextEngine.ConfidenceVal;
                    if (confidence>=0.65)
                    {
                        speechRecognized = true;
                        break;
                    }
                }
            }

            this.speechToTextEngine.DeactivateListenMode();
        }

        public void speechRecognitionWorker_RunWorkerComplete(object sender, RunWorkerCompletedEventArgs e)
        {
            if (listenWorkerCancelled)
            {
                //gui.presentFeedback("Listen Worker Cancelled");
                listenWorkerCancelled = false;
            }
            else if (speechRecognized)
            {
                //gui.presentFeedback("Speech Recognized");
                if (this.speechToTextEngine.WordStr != "UNKNOWN")
                {
                    float confidence = this.speechToTextEngine.ConfidenceVal;
                    if (confidence < 0.79)
                    {
                        DidYouMeanDialog dialog = new DidYouMeanDialog(this.speechToTextEngine.WordStr, this.speechToTextEngine.ConfidenceVal,this.gui.ItemNames);
                        if (dialog.ShowDialog() == DialogResult.OK)
                        {
                            gui.IndicationProcess_Panel();
                            FindItem(new Item(dialog.SelectedItem));
                        }
                        else
                        {
                            gui.SpeechActivation_Panel();
                        }

                    }
                    else
                    {
                        gui.IndicationProcess_Panel();
                        FindItem(new Item(this.speechToTextEngine.WordStr));
                    }
                }
                else
                {
                    gui.WordNotRecognized_Panel();
                }
                //gui.IndicationProcess_Panel();


                speechRecognized = false;
            }
            else
            {
                //gui.presentFeedback("Speech Not Recognized");
                gui.WordNotRecognized_Panel();
                speechRecognized = false;
            }
        }

        /// <summary>
        /// Finds an item in the system.
        /// </summary>
        /// <param name="fItem">The item to be found.</param>
        private void FindItem(Item fItem)
        {
            if (fItem != null)
            {
                bool isFound = findItemController.FindItem(fItem);

                if (isFound)
                {
                    gui.presentFeedback(String.Format("\"{0}\" was found", fItem.GetName()));
                    findItemController.LocateItem(fItem);
                }
                else
                {
                    gui.presentFeedback("I'm sorry. I could not find the item for you.");
                }
            }
        }

        /// <summary>
        /// This remove items from the system.
        /// </summary>
        /// <param name="item">Item to be removed from the system.</param>
        /// <param name="location">Location of Item to be removed from the system.</param>
        private void RemoveItem(Item item, Drawer location)
        {
            if ((item != null) && (location != null))
            {
                bool isRemoved = removeItemController.RemoveItem(item, location);
                if (isRemoved)
                {
                    gui.presentFeedback("\"" + item.GetName() + "\" was removed");
                    this.GetAllItems();
                }
                else
                {
                    gui.presentFeedback("\"" + item.GetName() + "\" could not be removed");
                }
            }
        }

        /// <summary>
        /// Adds item to the system.
        /// </summary>
        /// <param name="item">Item to be added to system.</param>
        /// <param name="location">Location of Item to be added to system.</param>
        private void AddItem(Item item, Drawer location)
        {
            if ((item != null) && (location != null))
            {
                gui.presentFeedback("The item being added is " + item.GetName() + " at location. Row:" + location.GetRow() + " Col: " + location.GetColumn());

                bool isAdded = addItemController.AddItem(item, location);

                if (isAdded)
                {
                    gui.presentFeedback("\"" + item.GetName() + "\" was added");
                    this.GetAllItems();
                }
                else
                {
                    gui.presentFeedback("\"" + item.GetName() + "\" could not be added");
                }
            }
        }

        /// <summary>
        /// Controls the process of moving an item, provides feedback as to success of operation.
        /// </summary>
        /// <param name="mItem">Item to move.</param>
        /// <param name="fromLocation">Location to move item from.</param>
        /// <param name="toLocation">Location to move item to.</param>
        private void MoveItem(Item mItem, Drawer fromLocation, Drawer toLocation)
        {
            //The code below checks first that the item can be added, if it can it adds it and trys to delete it from its previous location.
            if( (mItem != null) && (fromLocation != null) && (toLocation != null) )
            {
                bool isAdded = this.addItemController.AddItem(mItem, toLocation);

                if (isAdded == true)
                {
                    bool isRemoved = this.removeItemController.RemoveItem(mItem, fromLocation);

                    if (isRemoved == true)
                    {
                        this.GetAllItems();
                    }
                    else
                    {
                        this.removeItemController.RemoveItem(mItem, toLocation);
                    }
                }
                else
                {
                    this.gui.presentFeedback("Item couldn't be added to new location");
                }
            }
        }

        /// <summary>
        /// This method updates the GUI and SpeechToTextEngine to contain the current available items in the system.
        /// </summary>
        private void GetAllItems()
        {
            List<Drawer> tempDrawers = loadItemsController.LoadItems();

            List<string> tempItemNames = new List<string>();
            List<List<string>> locations = new List<List<string>>();

            for (int i = 0; i < tempDrawers.Count(); i++)
            {
                for (int j = 0; j < tempDrawers[i].GetItems().Count(); j++)
                {
                    if (tempItemNames.Contains(tempDrawers[i].GetItems()[j].GetName()))
                    {
                        locations[tempItemNames.IndexOf(tempDrawers[i].GetItems()[j].GetName())].Add(tempDrawers[i].GetRow().ToString() + "," + tempDrawers[i].GetColumn());
                    }
                    else
                    {
                        tempItemNames.Add(tempDrawers[i].GetItems()[j].GetName());
                        locations.Add(new List<string>());
                        locations[locations.Count() - 1].Add(tempDrawers[i].GetRow().ToString() + "," + tempDrawers[i].GetColumn());
                    }
                }
            }

            string[][] tempItemLocations = new string[locations.Count()][];

            for (int i = 0; i < locations.Count(); i++)
            {
                tempItemLocations[i] = locations[i].ToArray();
            }


            this.speechToTextEngine.UpdateDictionary(tempItemNames.ToArray());
            this.gui.UpdateItemMap(tempItemNames.ToArray(), tempItemLocations, tempDrawers.ToArray());
        }

        /// <summary>
        /// This method tests all the lights available in this system.
        /// </summary>
        private void TestLED(LED led)
        {
            testLEDController.TestLED(led);
        }

        /// <summary>
        /// This method notifies the GUI to start running.
        /// </summary>
        public void startApplication()
        {
            System.Windows.Forms.Application.Run(gui);
        }

        public void CloseApplication()
        {
            this.testLEDController.CloseLightsApplication();
        }
    }
}
